1 Introduction

  • Loan Default Data - aim at analyzing loan default trends and attributes in order to enhance future loans underwriting.
  • Data Collection Process - Process of generating the data is unknown,however, it is most likely collected from lending company nor from credit bureau.

1.1 Description of Data

  • Data Structure - dataset contains 1000 observations with 16 categorical and numerical variables.

  • Itemized List of Feature Variables:

    • Default Categorical Variable indicates whether the account is on good standing or not
    • Checking_amount Numerical Checking Account Balance
    • Term Numerical Loan Term
    • Credit_score Numerical Customer Credit Score
    • Gender Categorical Customer Gender
    • Marital_status Categorical Customer Marital Status
    • Car_loan Categorical Indicator for Personal loan (1 yes, 0 No)
    • Personal_loan Categorical Indicator for Personal loan (1 yes, 0 No)
    • Home_loan Categorical Indicator for Home loan (1 yes, 0 No)
    • Education_loan Categorical Indicator for student loan (1 yes, 0 No)
    • Emp_status Categorical Employment status (1 employed, 0 Notemployed)
    • Amount Numerical Loan Amount
    • Saving_amount Numerical Saving Amount
    • Emp_duration Numerical Number of years employed
    • Age Numerical Customer Age
    • No_of_credit_acc Numerical No of loans the custom have

1.2 Purposes of Using This Data Set

  • Look into visual presentation of data to provide better understand of it and highlight insights into the outcome that can be driven from the data.

Problem Statements: Report aim to analyse Loan Default Dataset to estimate the probaility of an accounts going into default using other variables as predictor and henc enhance loans underwriting.

2 Data Distribution

The report aim to look at the accounts distribution by different demographic in order to identify outliers and asses the impact and correlation between demographic characteristics and probability of account going into default.

2.1 Accounts Distribution

  • Age Groups* - Distribution of accounts Age group shows concentration in age group 26-35 where majority of accounts, ~ 70% concentration in that age group.
  • Employment Years* - Accounts spred across employments years between < 1 year and up to 10 years with very few have more than 10 years.
  • Loan Terms* - Accounts shows normal distribution by loan terms.
  • Number of Credit Accounts* - Majority of accounts have 1 credit account only, ~ 60+%.
  • Gender* - We can see concentration in Males compared to females.
  • Marital Status* - No concentration can be seen by marital status, though more married individuals than singles.
  • Employment status* - ~ 30% of accounts are unemployed.
  • Default* - Data shows high number of defaulted accounts.
LDD <- read.csv("https://raw.githubusercontent.com/sameralzaim/W02/refs/heads/main/BankLoanDefaultDataset.csv")
#View (LDD)
library(gridExtra)  # Load the package before using grid.arrange()


ldd <- LDD %>%
  mutate(Age_Group = cut(Age, 
                         breaks = c(18, 25, 35, 45, 55, 65, Inf), 
                         labels = c("18-25", "26-35", "36-45", "46-55", "56-65", "65+"),
                         right = FALSE))  # Right = FALSE means 25 is in "18-25"

# Create bar plot for Age Group
age_plot <- ggplot(ldd, aes(x = Age_Group, fill = Age_Group)) +
  geom_bar(fill = "navy") +
  labs(title = "No. of Acct by Age",
       x = "Age Group",
       y = "Count of Accounts") +
  theme_minimal() +
  theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Group Employment Duration into bins
ldd <- ldd %>%
  mutate(Emp_duration = cut(Emp_duration, 
                            breaks = c(0, 12, 24, 48, 96, 120, Inf), 
                            labels = c("< 1", "1-2", "2-4", "4-8", "8-10", "10+"),
                            right = FALSE))  # Right = FALSE means 24 is in "12-24"

# Create bar plot for Employment Duration
emp_plot <- ggplot(ldd, aes(x = Emp_duration, fill = Emp_duration)) +
  geom_bar(fill = "navy") +
  labs(title = "No. of Acct by Emp Years",
       x = "Employment Years",
       y = "Count of Accounts") +
  theme_minimal() +
  theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Create bar plot for Loan Term
term_plot <- ggplot(ldd, aes(x = Term, fill = Term)) +
  geom_bar(fill = "navy") +
  labs(title = "No. of Acct by Loan Term",
       x = "Loan Term",
       y = "Count of Accounts") +
  theme_minimal() +
  theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

ldd <- LDD %>%
  mutate(No_of_credit_acc = cut(No_of_credit_acc, 
                         breaks = c(1, 3, 5, 7, Inf), 
                         labels = c("1", "1-3", "3-5", "7+"),
                         right = FALSE))  # Right = FALSE means 25 is in "18-25"

# Create bar plot for No. of credit accounts
credit_plot <- ggplot(ldd, aes(x = No_of_credit_acc, fill = No_of_credit_acc)) +
  geom_bar(fill = "navy") +
  labs(title = "No. of Acct by No of credit acc",
       x = "No of Credit Acc",
       y = "Count of Accounts") +
  theme_minimal() +
  theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Arrange the three plots side by side

grid.arrange(age_plot, emp_plot, term_plot,credit_plot, ncol = 4)

library(gridExtra)

# Bar plot for Default
Default_plot <- ggplot(LDD, aes(x = as.factor(Default))) +
  geom_bar(fill = "brown2", color = "black") +
  labs(title = "No. of Acct by Default",
       x = "Default",
       y = "Number of Accounts") +
  theme_minimal()+
   theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Bar plot for Gender
gender_plot <- ggplot(LDD, aes(x = Gender)) +
  geom_bar(fill = "cyan4", color = "black") +
  labs(title = "No. of Acct by Gender",
       x = "Gender",
       y = "Number of Accounts") +
  theme_minimal()+
   theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Bar plot for Marital Status
marital_plot <- ggplot(LDD, aes(x = Marital_status)) +
  geom_bar(fill = "cyan4", color = "black") +
  labs(title = "No. of Acct by Marital Status",
       x = "Marital Status",
       y = "Number of Accounts") +
  theme_minimal()+
   theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Bar plot for Employment Status
emp_status_plot <- ggplot(LDD, aes(x = Emp_status)) +
  geom_bar(fill = "cyan4", color = "black") +
  labs(title = "No. of Acct by Emp Status",
       x = "Employment Status",
       y = "Number of Accounts") +
  theme_minimal()+
   theme(plot.title = element_text(size = 9, face = "bold", hjust = 0.5), legend.position = "none")  # Remove redundant legend

# Arrange the three plots side by side
grid.arrange(gender_plot, marital_plot, emp_status_plot,Default_plot, ncol = 4)

2.2 Missing values**

  • The graph below shows no missing values across all variables
my_data <- as.data.frame(LDD)

# Add row index for plotting
my_data$row_id <- seq_len(nrow(my_data))

# Convert missing values into a binary indicator
missing_data <- my_data %>%
  mutate(across(everything(), ~ ifelse(is.na(.), 1, 0))) %>%
  pivot_longer(cols = -row_id, names_to = "Variable", values_to = "Missing")

# Plot missing values as a heatmap
ggplot(missing_data, aes(x = Variable, y = row_id)) +
  geom_tile(aes(fill = as.factor(Missing))) +
  scale_fill_manual(values = c("white", "red"), labels = c("Present", "Missing")) +
  labs(title = "Missing Data Heatmap", x = "Variables", y = "Observations") +
  theme_minimal()

2.3 Distribution of Default Accounts

  • No particular trend emerges except that more default can be seen in lower age groups, femals, singles and accounts without personal loans
ldd_long <- LDD %>%
  mutate(across(everything(), as.character)) %>%  # Convert all columns to character
  pivot_longer(cols = -Default, names_to = "Variable", values_to = "Value")

# Generate distribution plots for each variable by Default
ggplot(ldd_long, aes(x = Value, fill = as.factor(Default))) +
  geom_bar(alpha = 0.7, position = "dodge") +  # Bar plot for categorical values
  facet_wrap(~ Variable, scales = "free") +  # Separate plots for each variable
  labs(title = "Distribution of Variables by number Defaulted Accounts",
       x = "Value",
       y = "Default",
       fill = "Default") +
  theme_minimal()

  • Distribution of Default Accounts by Gender and Marital status

    • No particular trend emerges
ds <- LDD
ds %>%
  filter(Default %in% c(0, 1)) %>%
  group_by(Gender, Marital_status, Default) %>%
  summarise(n = n(), .groups = "drop") %>%
  ggplot(aes(x = Gender, y = n, fill = factor(Default))) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_grid(. ~ Marital_status) +  # Side-by-side graphs
  ggtitle("Number of Accounts with Default (0 vs 1) by Gender and Marital Status") +
  labs(fill = "Default", x = "Gender", y = "Number of Accounts") +  
  scale_fill_manual(values = c("1" = "brown2", "0" = "cyan4")) +  # Balanced Red-Green colors
  theme_minimal() + 
  theme(
    panel.background = element_rect(fill = "#F0F0F0", color = NA),  # Gray inside graph only
    plot.background = element_blank(),   # Keep x/y labels and titles clean (no gray)
    strip.background = element_rect(fill = "#D3D3D3", color = "#D3D3D3"),  # Lighter gray facet headers
    strip.text = element_text(color = "black", face = "bold"),  # Black bold text for headers
    panel.spacing.x = unit(2, "lines"),  # THICK white separation between graphs
    panel.grid.major = element_line(color = "white", size = 0.7),  # Thin white major grid lines
    panel.grid.minor = element_line(color = "white", size = 0.7)   # Thin white minor grid lines
  )

3 Creating Missing accounts

for the sake of the analysis we will create missing values in saving amount and calculate the missing values

3.1 Confirming number of missing values:

sum(is.na(lddm$Saving_amount))
[1] 90

4 Imputing Missing Values

4.1 Pairing Variables with Missing Values with Predicting Variables

We start by pairing the Variable to get sense of the potential relationship. From below we can see that Amount and saving acount has similar distribution. Same observation can be seen also with marital status.

4.2 Imputing Missing Values Using Random Regression Imputation

  • Fit a linear model
  • Predict Saving_amount for the missing values
  • Get residuals from the linear model
  • Build the scatterplot to compare regression and random regression models
# Fit a linear model
pred.mdl <- lm(Saving_amount ~ Emp_duration + Marital_status + Amount + Credit_score, 
               data = lddm, na.action = na.exclude)

# Identify missing values in Saving_amount
missing_idx <- which(is.na(lddm$Saving_amount))  # Get missing value indices

# Predict Saving_amount only for missing values
pred.Saving_amount <- predict(pred.mdl, newdata = lddm[missing_idx, ])  

# Assign predicted values to missing rows
lddm$Saving_amount[missing_idx] <- pred.Saving_amount

# Ensure prediction length matches missing values count
if (length(pred.Saving_amount) != length(missing_idx)) {
    stop("Prediction length does not match the number of missing values.")
}

# Get residuals from the linear model
pred.resid <- resid(pred.mdl)

# Ensure enough residuals are available for sampling
m0 <- length(pred.Saving_amount)  
if (length(pred.resid) >= m0) {
    pred.yrand <- pred.Saving_amount + sample(pred.resid, m0, replace = TRUE)
} else {
    pred.yrand <- pred.Saving_amount  # Use deterministic values as fallback
}

# Assign the final imputed values
lddm$Saving_amount[missing_idx] <- pred.yrand

# Check if all missing values are imputed
remaining_na <- sum(is.na(lddm$Saving_amount))


# Scatter plot
plot(lddm$Checking_amount, lddm$Saving_amount, 
     main = "Saving_amount vs Checking_amount",
     xlab = "Checking Amount", ylab = "Saving Amount", col = "gray")

# Add regression-imputed points (red)
points(lddm$Checking_amount[missing_idx], pred.Saving_amount, pch = 19, col = "red")

# Add random regression-imputed points (blue)
points(lddm$Checking_amount[missing_idx], pred.yrand, pch = 19, col = "blue")

# Add legend
legend("topleft", legend = c("Regression Imputation", "Random Regression Imputation"),
       col = c("red", "blue"), pch = rep(19, 2), bty = "n", cex = 0.8)

  • Confirming that all missing values were assigned imputed values. we have zero missing values after imputaion as per below.
# Update the dataset with the imputed values for Saving_amount
missing_idx <- which(is.na(lddm$Saving_amount))
lddm$Saving_amount[missing_idx] <- pred.Saving_amount[missing_idx]

# Count missing values after imputation
missing_after <- sum(is.na(lddm$Saving_amount))
print(paste("Missing values after imputation:", missing_after))
[1] "Missing values after imputation: 0"

5 Fitting The Logestic Regression Model For Default

# Load necessary library


# Fit a full logistic regression model with all predictors
full_model <- glm(Default ~ ., data = lddm, family = binomial)

# Perform stepwise selection (default is backward elimination)
step_model <- step(full_model, direction = "both")
Start:  AIC=346.23
Default ~ Checking_amount + Term + Credit_score + Gender + Marital_status + 
    Car_loan + Personal_loan + Home_loan + Education_loan + Emp_status + 
    Amount + Saving_amount + Emp_duration + Age + No_of_credit_acc

                   Df Deviance    AIC
- Gender            1   314.24 344.24
- Car_loan          1   314.30 344.30
- Education_loan    1   314.33 344.33
- Marital_status    1   314.40 344.40
- Personal_loan     1   314.60 344.60
- Emp_duration      1   314.85 344.85
- No_of_credit_acc  1   315.24 345.24
<none>                  314.23 346.23
- Home_loan         1   316.30 346.30
- Amount            1   316.80 346.80
- Emp_status        1   317.45 347.45
- Term              1   326.99 356.99
- Credit_score      1   347.61 377.61
- Saving_amount     1   391.55 421.55
- Checking_amount   1   391.88 421.88
- Age               1   547.07 577.07

Step:  AIC=344.24
Default ~ Checking_amount + Term + Credit_score + Marital_status + 
    Car_loan + Personal_loan + Home_loan + Education_loan + Emp_status + 
    Amount + Saving_amount + Emp_duration + Age + No_of_credit_acc

                   Df Deviance    AIC
- Car_loan          1   314.30 342.30
- Education_loan    1   314.34 342.34
- Marital_status    1   314.51 342.51
- Personal_loan     1   314.61 342.61
- Emp_duration      1   314.86 342.86
- No_of_credit_acc  1   315.24 343.24
<none>                  314.24 344.24
- Home_loan         1   316.31 344.31
- Amount            1   316.82 344.82
- Emp_status        1   317.47 345.47
+ Gender            1   314.23 346.23
- Term              1   327.17 355.17
- Credit_score      1   347.65 375.65
- Saving_amount     1   391.55 419.55
- Checking_amount   1   391.91 419.91
- Age               1   547.23 575.23

Step:  AIC=342.3
Default ~ Checking_amount + Term + Credit_score + Marital_status + 
    Personal_loan + Home_loan + Education_loan + Emp_status + 
    Amount + Saving_amount + Emp_duration + Age + No_of_credit_acc

                   Df Deviance    AIC
- Marital_status    1   314.57 340.57
- Emp_duration      1   314.90 340.90
- No_of_credit_acc  1   315.29 341.29
<none>                  314.30 342.30
- Amount            1   316.89 342.89
- Emp_status        1   317.56 343.56
+ Car_loan          1   314.24 344.24
+ Gender            1   314.30 344.30
- Personal_loan     1   321.57 347.57
- Education_loan    1   321.92 347.92
- Term              1   327.20 353.20
- Home_loan         1   330.46 356.46
- Credit_score      1   347.70 373.70
- Checking_amount   1   391.92 417.92
- Saving_amount     1   392.12 418.12
- Age               1   547.68 573.68

Step:  AIC=340.57
Default ~ Checking_amount + Term + Credit_score + Personal_loan + 
    Home_loan + Education_loan + Emp_status + Amount + Saving_amount + 
    Emp_duration + Age + No_of_credit_acc

                   Df Deviance    AIC
- Emp_duration      1   315.02 339.02
- No_of_credit_acc  1   315.72 339.72
<none>                  314.57 340.57
- Amount            1   317.09 341.09
- Emp_status        1   317.61 341.61
+ Marital_status    1   314.30 342.30
+ Gender            1   314.46 342.46
+ Car_loan          1   314.51 342.51
- Personal_loan     1   321.76 345.76
- Education_loan    1   322.27 346.27
- Term              1   328.06 352.06
- Home_loan         1   330.65 354.65
- Credit_score      1   347.91 371.91
- Checking_amount   1   392.16 416.16
- Saving_amount     1   392.70 416.70
- Age               1   548.37 572.37

Step:  AIC=339.02
Default ~ Checking_amount + Term + Credit_score + Personal_loan + 
    Home_loan + Education_loan + Emp_status + Amount + Saving_amount + 
    Age + No_of_credit_acc

                   Df Deviance    AIC
- No_of_credit_acc  1   316.03 338.03
<none>                  315.02 339.02
- Amount            1   317.60 339.60
- Emp_status        1   318.45 340.45
+ Emp_duration      1   314.57 340.57
+ Marital_status    1   314.90 340.90
+ Gender            1   314.96 340.96
+ Car_loan          1   314.98 340.98
- Personal_loan     1   322.17 344.17
- Education_loan    1   322.56 344.56
- Term              1   328.53 350.53
- Home_loan         1   331.25 353.25
- Credit_score      1   347.93 369.93
- Checking_amount   1   392.17 414.17
- Saving_amount     1   392.87 414.87
- Age               1   549.80 571.80

Step:  AIC=338.03
Default ~ Checking_amount + Term + Credit_score + Personal_loan + 
    Home_loan + Education_loan + Emp_status + Amount + Saving_amount + 
    Age

                   Df Deviance    AIC
<none>                  316.03 338.03
- Amount            1   318.52 338.52
+ No_of_credit_acc  1   315.02 339.02
- Emp_status        1   319.07 339.07
+ Emp_duration      1   315.72 339.72
+ Marital_status    1   315.78 339.78
+ Gender            1   315.91 339.91
+ Car_loan          1   316.01 340.01
- Personal_loan     1   323.38 343.38
- Education_loan    1   323.46 343.46
- Term              1   329.33 349.33
- Home_loan         1   332.49 352.49
- Credit_score      1   348.73 368.73
- Checking_amount   1   394.33 414.33
- Saving_amount     1   395.76 415.76
- Age               1   552.47 572.47

6 Predicting the Default

From the all model logistic regression step above, we got our primary model and we use cross-validation is to compare performance between this model and another challenger model to arrive at optimal model selection, we will use the following two logistic regression models and use the 5-fold cross-validation method to identify the optimal model.

  • Model 1: Default = α0+ α1×Checking_amount + α2×Term + α3×Credit_score + α4×Personal_loan + α5×Home_loan + α6×Education_loan + α7×Emp_status + α8×Saving_amount + α9×Age

  • Model 2: Default = α0+ α1×Credit_score + α2×Home_loan + α3×Education_loan + α4×Emp_status + α5×Saving_amount + α6×Age

Model Selection via 5-fold Cross-Validation

  • We use 5-fold cross-validation to select the better one from Model 1 and Model 2. 5-fold cross-validation: splitting the training set into 5 folds with equal size to perform cross-validation.

  • Data Splitting: Using random splitting to partition the data into training set (700) and testing set (300).

  • Choose Performance Measure: The resulting model will be used to predict the Default. We use the mean square error to measure the predictive performance.

# Load the data
data(LDD)
#LDD$am <- as.factor(mtcars$am) # Convert to factor for logistic regression

# Fit a logistic regression model
model_glm <- glm(Default ~ Checking_amount + Term + Credit_score + Personal_loan + 
    Home_loan + Education_loan + Emp_status + Saving_amount + 
    Age, family = binomial, data = LDD)

# Summary of the model
summary(model_glm)

Call:
glm(formula = Default ~ Checking_amount + Term + Credit_score + 
    Personal_loan + Home_loan + Education_loan + Emp_status + 
    Saving_amount + Age, family = binomial, data = LDD)

Coefficients:
                       Estimate Std. Error z value Pr(>|z|)    
(Intercept)          39.9793055  3.8250697  10.452  < 2e-16 ***
Checking_amount      -0.0051043  0.0006725  -7.590 3.21e-14 ***
Term                  0.1714373  0.0515333   3.327 0.000879 ***
Credit_score         -0.0107846  0.0020565  -5.244 1.57e-07 ***
Personal_loan        -0.9157256  0.3307825  -2.768 0.005634 ** 
Home_loan            -2.8298877  0.7711243  -3.670 0.000243 ***
Education_loan        1.3059092  0.5483430   2.382 0.017240 *  
Emp_statusunemployed  0.5855966  0.3339530   1.754 0.079511 .  
Saving_amount        -0.0047747  0.0006035  -7.912 2.54e-15 ***
Age                  -0.6504636  0.0632505 -10.284  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 1221.7  on 999  degrees of freedom
Residual deviance:  301.9  on 990  degrees of freedom
AIC: 321.9

Number of Fisher Scoring iterations: 7
# Install plotly if not already installed
if (!requireNamespace("plotly", quietly = TRUE)) {
  install.packages("plotly")
}

# Load the package
library(plotly)

# using sample() to perform random splitting
train.ID = sample(1:dim(lddm)[1], 700, replace = FALSE)  # without replacement
# training set
train = lddm[train.ID,]
test = lddm[-train.ID,]
## splitting the train set into 5 folds to train and validate the candidate models
N = dim(train)[1]   # size of training data
k = 5               # number of folds
fld.n = ceiling(N/k)
MSE.m1 = NULL       # null vector to store MSE
MSE.m2 = NULL      
for (i in 1:k){
  valid.ID = ((i-1)*fld.n +1):(i*fld.n)  # observation ID for the i-th validation set 
  valid.set = train[valid.ID, ]
  train.set = train[-valid.ID,]
  ## fitting two candidate models with combined 4 folds of data set
  M01 = glm(Default ~ Checking_amount + Term + Credit_score + Personal_loan + Home_loan + Education_loan + Emp_status + Saving_amount + Age, family = binomial, data = train.set)
  M02 = glm(Default ~ Credit_score + Home_loan + Education_loan + Emp_status + Saving_amount + Age, family = binomial, data = train.set)
  ## Predicting Default using the two candidate models based on the validate set
  predM01 = predict(M01, newdata = valid.set)
  predM02 = predict(M02, newdata = valid.set)
  ## calculating the MSE associated with the two models
  MSE.m1[i] = mean((predM01 - valid.set$Default)^2)
  MSE.m2[i] = mean((predM02 - valid.set$Default)^2)
}
## define a data frame to store the MSE of the candidate models
## 
MSE = data.frame(fold = rep(1:k,2), MSE = c(MSE.m1, MSE.m2), type=c(rep("Model 1",k), rep("Model 2", k)))

## line plots of the 
cvplot = ggplot(data = MSE, aes(x=fold, y=MSE, color = type)) +
         geom_line() +
         geom_point() +
         coord_cartesian(xlim = c(0, 6),
                         ylim = c(0,40)) +
         geom_text(mapping = aes(x=2.0, y=5, 
                  label=paste("Model 1 Mean MSE: = ", round(mean(MSE.m1),3), "")), 
                   hjust=0) +
         geom_text(mapping = aes(x=2.0, y=10, 
                  label=paste("Model 2 Mean MSE: = ", round(mean(MSE.m2),3), "")), 
                   hjust=0) + 
         ggtitle("Line plots of MSE candidate Models across folds") +
         theme(plot.title = element_text(hjust = 0.5),
               plot.margin = unit(c(1,1,1,1), "cm"))
ggplotly(cvplot)

7 Conclusion

from the above we can see that Model 2 has lower MSE and more simpler, with 6 predictors, compared to model 1 and hence we choose model 2 as Default predictor.

LS0tDQp0aXRsZTogJ0RlZXAgRGl2ZSBJbnRvIExvYW4gRGVmYXVsdCBEYXRhIHRvIEFjZXNzIERlZmF1bHQgRHJpdmVycycNCmF1dGhvcjogIlNhbWVyIEFsemFpbSINCmRhdGU6ICJXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogMw0KICAgIGZpZ19oZWlnaHQ6IDMNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHs9aHRtbH0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovDQoNCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBBcmlhbDsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIHRoZSBkYXRlICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBBcmlhbCwgQXJpYWw7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJBcmlhbCIsIEFyaWFsLCBBcmlhbDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxNnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiQXJpYWwiLCBBcmlhbCwgQXJpYWw7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMTRweDsNCiAgICBmb250LWZhbWlseTogIkFyaWFsIiwgQXJpYWwsIEFyaWFsOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCi5oaWdobGlnaHRtZSB7IGJhY2tncm91bmQtY29sb3I6eWVsbG93OyB9DQoNCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9DQoNCjwvc3R5bGU+DQpgYGANCg0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoImtlcm5sYWIiKSkgeyAjIFNWTSBtZXRob2RvbG9neQ0KICAgaW5zdGFsbC5wYWNrYWdlcygia2VybmxhYiIpDQpsaWJyYXJ5KGtlcm5sYWIpDQp9DQppZiAoIXJlcXVpcmUoImUxMDcxIikpIHsgIyBTVk0gbWV0aG9kb2xvZ3kNCiAgIGluc3RhbGwucGFja2FnZXMoImUxMDcxIikNCmxpYnJhcnkoZTEwNzEpDQp9DQppZiAoIXJlcXVpcmUoIklTTFIiKSkgeyAjIGNvbnRhaW5zIGV4YW1wbGUgZGF0YSBzZXQgIktoYW4iDQogICBpbnN0YWxsLnBhY2thZ2VzKCJJU0xSIikNCmxpYnJhcnkoSVNMUikNCn0NCmlmICghcmVxdWlyZSgiUkNvbG9yQnJld2VyIikpIHsgIyBjdXN0b21pemVkIGNvbG9yaW5nIG9mIHBsb3RzDQogICBpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQp9DQppZiAoIXJlcXVpcmUoIlZJTSIpKSB7ICMgU1ZNIG1ldGhvZG9sb2d5DQogICBpbnN0YWxsLnBhY2thZ2VzKCJWSU0iKQ0KbGlicmFyeShWSU0pDQp9DQppZiAoIXJlcXVpcmUoIm1pY2UiKSkgeyAjIFNWTSBtZXRob2RvbG9neQ0KICAgaW5zdGFsbC5wYWNrYWdlcygibWljZSIpDQpsaWJyYXJ5KG1pY2UpDQp9DQppZiAoIXJlcXVpcmUoImdncHViciIpKSB7ICMgU1ZNIG1ldGhvZG9sb2d5DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3B1YnIiKQ0KbGlicmFyeShnZ3B1YnIpDQp9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEpICANCmBgYA0KDQojIEludHJvZHVjdGlvbg0KICArICpMb2FuIERlZmF1bHQgRGF0YSogLSBhaW0gYXQgYW5hbHl6aW5nIGxvYW4gZGVmYXVsdCB0cmVuZHMgYW5kIGF0dHJpYnV0ZXMgaW4gb3JkZXIgdG8gZW5oYW5jZSBmdXR1cmUgbG9hbnMgdW5kZXJ3cml0aW5nLg0KICArICpEYXRhIENvbGxlY3Rpb24gUHJvY2VzcyogLSBQcm9jZXNzIG9mIGdlbmVyYXRpbmcgdGhlIGRhdGEgaXMgdW5rbm93bixob3dldmVyLCBpdCBpcyBtb3N0IGxpa2VseSBjb2xsZWN0ZWQgZnJvbSBsZW5kaW5nIGNvbXBhbnkgbm9yIGZyb20gY3JlZGl0IGJ1cmVhdS4NCiAgICANCiMjIERlc2NyaXB0aW9uIG9mIERhdGENCg0KICArICpEYXRhIFN0cnVjdHVyZSogLSBkYXRhc2V0IGNvbnRhaW5zIDEwMDAgb2JzZXJ2YXRpb25zIHdpdGggMTYgY2F0ZWdvcmljYWwgYW5kIG51bWVyaWNhbCB2YXJpYWJsZXMuIA0KICArICpJdGVtaXplZCBMaXN0IG9mIEZlYXR1cmUgVmFyaWFibGVzKjoNCiAgDQogICAgLSBEZWZhdWx0ICAgICAgICAgICAgICAgQ2F0ZWdvcmljYWwgICBWYXJpYWJsZSBpbmRpY2F0ZXMgd2hldGhlciB0aGUgYWNjb3VudCBpcyBvbiBnb29kIHN0YW5kaW5nIG9yIG5vdA0KICAgIC0gQ2hlY2tpbmdfYW1vdW50ICAgICAgIE51bWVyaWNhbCAgICAgQ2hlY2tpbmcgQWNjb3VudCBCYWxhbmNlIA0KICAgIC0gVGVybSAgICAgICAgICAgICAgICAgIE51bWVyaWNhbCAgICAgTG9hbiBUZXJtDQogICAgLSBDcmVkaXRfc2NvcmUgICAgICAgICAgTnVtZXJpY2FsICAgICBDdXN0b21lciBDcmVkaXQgU2NvcmUNCiAgICAtIEdlbmRlciAgICAgICAgICAgICAgICBDYXRlZ29yaWNhbCAgIEN1c3RvbWVyIEdlbmRlcg0KICAgIC0gTWFyaXRhbF9zdGF0dXMgICAgICAgIENhdGVnb3JpY2FsICAgQ3VzdG9tZXIgTWFyaXRhbCBTdGF0dXMgDQogICAgLSBDYXJfbG9hbiAgICAgICAgICAgICAgQ2F0ZWdvcmljYWwgICBJbmRpY2F0b3IgZm9yIFBlcnNvbmFsIGxvYW4gKDEgeWVzLCAwIE5vKQ0KICAgIC0gUGVyc29uYWxfbG9hbiAgICAgICAgIENhdGVnb3JpY2FsICAgSW5kaWNhdG9yIGZvciBQZXJzb25hbCBsb2FuICgxIHllcywgMCBObykNCiAgICAtIEhvbWVfbG9hbiAgICAgICAgICAgICBDYXRlZ29yaWNhbCAgIEluZGljYXRvciBmb3IgSG9tZSBsb2FuICgxIHllcywgMCBObykNCiAgICAtIEVkdWNhdGlvbl9sb2FuICAgICAgICBDYXRlZ29yaWNhbCAgIEluZGljYXRvciBmb3Igc3R1ZGVudCBsb2FuICgxIHllcywgMCBObykNCiAgICAtIEVtcF9zdGF0dXMgICAgICAgICAgICBDYXRlZ29yaWNhbCAgIEVtcGxveW1lbnQgc3RhdHVzICgxIGVtcGxveWVkLCAwICAgICAgIE5vdGVtcGxveWVkKQ0KICAgIC0gQW1vdW50ICAgICAgICAgICAgICAgIE51bWVyaWNhbCAgICAgTG9hbiBBbW91bnQgICAgICAgDQogICAgLSBTYXZpbmdfYW1vdW50ICAgICAgICAgTnVtZXJpY2FsICAgICBTYXZpbmcgQW1vdW50DQogICAgLSBFbXBfZHVyYXRpb24gICAgICAgICAgTnVtZXJpY2FsICAgICBOdW1iZXIgb2YgeWVhcnMgZW1wbG95ZWQNCiAgICAtIEFnZSAgICAgICAgICAgICAgICAgICBOdW1lcmljYWwgICAgIEN1c3RvbWVyIEFnZQ0KICAgIC0gTm9fb2ZfY3JlZGl0X2FjYyAgICAgIE51bWVyaWNhbCAgICAgTm8gb2YgbG9hbnMgdGhlIGN1c3RvbSBoYXZlDQogICAgDQoNCg0KICANCiMjIFB1cnBvc2VzIG9mIFVzaW5nIFRoaXMgRGF0YSBTZXQNCg0KICArIExvb2sgaW50byB2aXN1YWwgcHJlc2VudGF0aW9uIG9mIGRhdGEgdG8gcHJvdmlkZSBiZXR0ZXIgdW5kZXJzdGFuZCBvZiBpdCBhbmQgaGlnaGxpZ2h0IGluc2lnaHRzIGludG8gdGhlIG91dGNvbWUgdGhhdCBjYW4gYmUgZHJpdmVuIGZyb20gdGhlIGRhdGEuDQogIA0KIDxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfVByb2JsZW0gU3RhdGVtZW50cyoqOio8L2ZvbnQ+ICAqICA8Zm9udCBjb2xvciA9ICJibHVlIj5cY29sb3J7Ymx1ZX1SZXBvcnQgYWltIHRvIGFuYWx5c2UgTG9hbiBEZWZhdWx0IERhdGFzZXQgdG8gZXN0aW1hdGUgdGhlIHByb2JhaWxpdHkgb2YgYW4gYWNjb3VudHMgZ29pbmcgaW50byBkZWZhdWx0IHVzaW5nIG90aGVyIHZhcmlhYmxlcyBhcyBwcmVkaWN0b3IgYW5kIGhlbmMgZW5oYW5jZSBsb2FucyB1bmRlcndyaXRpbmcuPC9mb250Pg0KDQojIERhdGEgRGlzdHJpYnV0aW9uDQoNClRoZSByZXBvcnQgYWltIHRvIGxvb2sgYXQgdGhlIGFjY291bnRzIGRpc3RyaWJ1dGlvbiBieSBkaWZmZXJlbnQgZGVtb2dyYXBoaWMgaW4gb3JkZXIgdG8gaWRlbnRpZnkgb3V0bGllcnMgYW5kIGFzc2VzIHRoZSBpbXBhY3QgYW5kIGNvcnJlbGF0aW9uIGJldHdlZW4gZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzIGFuZCBwcm9iYWJpbGl0eSBvZiBhY2NvdW50IGdvaW5nIGludG8gZGVmYXVsdC4NCg0KIyMgQWNjb3VudHMgRGlzdHJpYnV0aW9uDQoNCiAgLSBBZ2UgR3JvdXBzKiAtIERpc3RyaWJ1dGlvbiBvZiBhY2NvdW50cyBBZ2UgZ3JvdXAgc2hvd3MgY29uY2VudHJhdGlvbiBpbiBhZ2UgZ3JvdXAgMjYtMzUgd2hlcmUgbWFqb3JpdHkgb2YgYWNjb3VudHMsIH4gNzAlIGNvbmNlbnRyYXRpb24gaW4gdGhhdCBhZ2UgZ3JvdXAuDQogIC0gRW1wbG95bWVudCBZZWFycyogLSBBY2NvdW50cyBzcHJlZCBhY3Jvc3MgZW1wbG95bWVudHMgeWVhcnMgYmV0d2VlbiA8IDEgeWVhciBhbmQgdXAgdG8gMTAgeWVhcnMgd2l0aCB2ZXJ5IGZldyBoYXZlIG1vcmUgdGhhbiAxMCB5ZWFycy4NCiAgLSBMb2FuIFRlcm1zKiAtIEFjY291bnRzIHNob3dzIG5vcm1hbCBkaXN0cmlidXRpb24gYnkgbG9hbiB0ZXJtcy4NCiAgLSBOdW1iZXIgb2YgQ3JlZGl0IEFjY291bnRzKiAtIE1ham9yaXR5IG9mIGFjY291bnRzIGhhdmUgMSBjcmVkaXQgYWNjb3VudCBvbmx5LCB+IDYwKyUuDQogIC0gR2VuZGVyKiAtIFdlIGNhbiBzZWUgY29uY2VudHJhdGlvbiBpbiBNYWxlcyBjb21wYXJlZCB0byBmZW1hbGVzLg0KICAtIE1hcml0YWwgU3RhdHVzKiAtIE5vIGNvbmNlbnRyYXRpb24gY2FuIGJlIHNlZW4gYnkgbWFyaXRhbCBzdGF0dXMsIHRob3VnaCBtb3JlIG1hcnJpZWQgaW5kaXZpZHVhbHMgdGhhbiBzaW5nbGVzLg0KICAtIEVtcGxveW1lbnQgc3RhdHVzKiAtIH4gMzAlIG9mIGFjY291bnRzIGFyZSB1bmVtcGxveWVkLg0KICAtIERlZmF1bHQqIC0gRGF0YSBzaG93cyBoaWdoIG51bWJlciBvZiBkZWZhdWx0ZWQgYWNjb3VudHMuDQogICAgDQoNCmBgYHtyfQ0KTEREIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2FtZXJhbHphaW0vVzAyL3JlZnMvaGVhZHMvbWFpbi9CYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpDQojVmlldyAoTEREKQ0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdyaWRFeHRyYSkgICMgTG9hZCB0aGUgcGFja2FnZSBiZWZvcmUgdXNpbmcgZ3JpZC5hcnJhbmdlKCkNCg0KDQpsZGQgPC0gTEREICU+JQ0KICBtdXRhdGUoQWdlX0dyb3VwID0gY3V0KEFnZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygxOCwgMjUsIDM1LCA0NSwgNTUsIDY1LCBJbmYpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIxOC0yNSIsICIyNi0zNSIsICIzNi00NSIsICI0Ni01NSIsICI1Ni02NSIsICI2NSsiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICByaWdodCA9IEZBTFNFKSkgICMgUmlnaHQgPSBGQUxTRSBtZWFucyAyNSBpcyBpbiAiMTgtMjUiDQoNCiMgQ3JlYXRlIGJhciBwbG90IGZvciBBZ2UgR3JvdXANCmFnZV9wbG90IDwtIGdncGxvdChsZGQsIGFlcyh4ID0gQWdlX0dyb3VwLCBmaWxsID0gQWdlX0dyb3VwKSkgKw0KICBnZW9tX2JhcihmaWxsID0gIm5hdnkiKSArDQogIGxhYnModGl0bGUgPSAiTm8uIG9mIEFjY3QgYnkgQWdlIiwNCiAgICAgICB4ID0gIkFnZSBHcm91cCIsDQogICAgICAgeSA9ICJDb3VudCBvZiBBY2NvdW50cyIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSByZWR1bmRhbnQgbGVnZW5kDQoNCiMgR3JvdXAgRW1wbG95bWVudCBEdXJhdGlvbiBpbnRvIGJpbnMNCmxkZCA8LSBsZGQgJT4lDQogIG11dGF0ZShFbXBfZHVyYXRpb24gPSBjdXQoRW1wX2R1cmF0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDEyLCAyNCwgNDgsIDk2LCAxMjAsIEluZiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjwgMSIsICIxLTIiLCAiMi00IiwgIjQtOCIsICI4LTEwIiwgIjEwKyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0ID0gRkFMU0UpKSAgIyBSaWdodCA9IEZBTFNFIG1lYW5zIDI0IGlzIGluICIxMi0yNCINCg0KIyBDcmVhdGUgYmFyIHBsb3QgZm9yIEVtcGxveW1lbnQgRHVyYXRpb24NCmVtcF9wbG90IDwtIGdncGxvdChsZGQsIGFlcyh4ID0gRW1wX2R1cmF0aW9uLCBmaWxsID0gRW1wX2R1cmF0aW9uKSkgKw0KICBnZW9tX2JhcihmaWxsID0gIm5hdnkiKSArDQogIGxhYnModGl0bGUgPSAiTm8uIG9mIEFjY3QgYnkgRW1wIFllYXJzIiwNCiAgICAgICB4ID0gIkVtcGxveW1lbnQgWWVhcnMiLA0KICAgICAgIHkgPSAiQ291bnQgb2YgQWNjb3VudHMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyBSZW1vdmUgcmVkdW5kYW50IGxlZ2VuZA0KDQojIENyZWF0ZSBiYXIgcGxvdCBmb3IgTG9hbiBUZXJtDQp0ZXJtX3Bsb3QgPC0gZ2dwbG90KGxkZCwgYWVzKHggPSBUZXJtLCBmaWxsID0gVGVybSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJuYXZ5IikgKw0KICBsYWJzKHRpdGxlID0gIk5vLiBvZiBBY2N0IGJ5IExvYW4gVGVybSIsDQogICAgICAgeCA9ICJMb2FuIFRlcm0iLA0KICAgICAgIHkgPSAiQ291bnQgb2YgQWNjb3VudHMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyBSZW1vdmUgcmVkdW5kYW50IGxlZ2VuZA0KDQpsZGQgPC0gTEREICU+JQ0KICBtdXRhdGUoTm9fb2ZfY3JlZGl0X2FjYyA9IGN1dChOb19vZl9jcmVkaXRfYWNjLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDEsIDMsIDUsIDcsIEluZiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjEiLCAiMS0zIiwgIjMtNSIsICI3KyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0ID0gRkFMU0UpKSAgIyBSaWdodCA9IEZBTFNFIG1lYW5zIDI1IGlzIGluICIxOC0yNSINCg0KIyBDcmVhdGUgYmFyIHBsb3QgZm9yIE5vLiBvZiBjcmVkaXQgYWNjb3VudHMNCmNyZWRpdF9wbG90IDwtIGdncGxvdChsZGQsIGFlcyh4ID0gTm9fb2ZfY3JlZGl0X2FjYywgZmlsbCA9IE5vX29mX2NyZWRpdF9hY2MpKSArDQogIGdlb21fYmFyKGZpbGwgPSAibmF2eSIpICsNCiAgbGFicyh0aXRsZSA9ICJOby4gb2YgQWNjdCBieSBObyBvZiBjcmVkaXQgYWNjIiwNCiAgICAgICB4ID0gIk5vIG9mIENyZWRpdCBBY2MiLA0KICAgICAgIHkgPSAiQ291bnQgb2YgQWNjb3VudHMiKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyBSZW1vdmUgcmVkdW5kYW50IGxlZ2VuZA0KDQojIEFycmFuZ2UgdGhlIHRocmVlIHBsb3RzIHNpZGUgYnkgc2lkZQ0KDQpncmlkLmFycmFuZ2UoYWdlX3Bsb3QsIGVtcF9wbG90LCB0ZXJtX3Bsb3QsY3JlZGl0X3Bsb3QsIG5jb2wgPSA0KQ0KDQpgYGANCg0KDQpgYGAge3J9DQoNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KDQojIEJhciBwbG90IGZvciBEZWZhdWx0DQpEZWZhdWx0X3Bsb3QgPC0gZ2dwbG90KExERCwgYWVzKHggPSBhcy5mYWN0b3IoRGVmYXVsdCkpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiYnJvd24yIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnModGl0bGUgPSAiTm8uIG9mIEFjY3QgYnkgRGVmYXVsdCIsDQogICAgICAgeCA9ICJEZWZhdWx0IiwNCiAgICAgICB5ID0gIk51bWJlciBvZiBBY2NvdW50cyIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSByZWR1bmRhbnQgbGVnZW5kDQoNCiMgQmFyIHBsb3QgZm9yIEdlbmRlcg0KZ2VuZGVyX3Bsb3QgPC0gZ2dwbG90KExERCwgYWVzKHggPSBHZW5kZXIpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiY3lhbjQiLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJOby4gb2YgQWNjdCBieSBHZW5kZXIiLA0KICAgICAgIHggPSAiR2VuZGVyIiwNCiAgICAgICB5ID0gIk51bWJlciBvZiBBY2NvdW50cyIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSByZWR1bmRhbnQgbGVnZW5kDQoNCiMgQmFyIHBsb3QgZm9yIE1hcml0YWwgU3RhdHVzDQptYXJpdGFsX3Bsb3QgPC0gZ2dwbG90KExERCwgYWVzKHggPSBNYXJpdGFsX3N0YXR1cykpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJjeWFuNCIsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHRpdGxlID0gIk5vLiBvZiBBY2N0IGJ5IE1hcml0YWwgU3RhdHVzIiwNCiAgICAgICB4ID0gIk1hcml0YWwgU3RhdHVzIiwNCiAgICAgICB5ID0gIk51bWJlciBvZiBBY2NvdW50cyIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSByZWR1bmRhbnQgbGVnZW5kDQoNCiMgQmFyIHBsb3QgZm9yIEVtcGxveW1lbnQgU3RhdHVzDQplbXBfc3RhdHVzX3Bsb3QgPC0gZ2dwbG90KExERCwgYWVzKHggPSBFbXBfc3RhdHVzKSkgKw0KICBnZW9tX2JhcihmaWxsID0gImN5YW40IiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnModGl0bGUgPSAiTm8uIG9mIEFjY3QgYnkgRW1wIFN0YXR1cyIsDQogICAgICAgeCA9ICJFbXBsb3ltZW50IFN0YXR1cyIsDQogICAgICAgeSA9ICJOdW1iZXIgb2YgQWNjb3VudHMiKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyBSZW1vdmUgcmVkdW5kYW50IGxlZ2VuZA0KDQojIEFycmFuZ2UgdGhlIHRocmVlIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKGdlbmRlcl9wbG90LCBtYXJpdGFsX3Bsb3QsIGVtcF9zdGF0dXNfcGxvdCxEZWZhdWx0X3Bsb3QsIG5jb2wgPSA0KQ0KDQpgYGANCg0KIyMgTWlzc2luZyB2YWx1ZXMqKiANCg0KICAtIFRoZSBncmFwaCBiZWxvdyBzaG93cyBubyBtaXNzaW5nIHZhbHVlcyBhY3Jvc3MgYWxsIHZhcmlhYmxlcw0KDQpgYGB7cn0NCm15X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShMREQpDQoNCiMgQWRkIHJvdyBpbmRleCBmb3IgcGxvdHRpbmcNCm15X2RhdGEkcm93X2lkIDwtIHNlcV9sZW4obnJvdyhteV9kYXRhKSkNCg0KIyBDb252ZXJ0IG1pc3NpbmcgdmFsdWVzIGludG8gYSBiaW5hcnkgaW5kaWNhdG9yDQptaXNzaW5nX2RhdGEgPC0gbXlfZGF0YSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIH4gaWZlbHNlKGlzLm5hKC4pLCAxLCAwKSkpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IC1yb3dfaWQsIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gIk1pc3NpbmciKQ0KDQojIFBsb3QgbWlzc2luZyB2YWx1ZXMgYXMgYSBoZWF0bWFwDQpnZ3Bsb3QobWlzc2luZ19kYXRhLCBhZXMoeCA9IFZhcmlhYmxlLCB5ID0gcm93X2lkKSkgKw0KICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBhcy5mYWN0b3IoTWlzc2luZykpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIndoaXRlIiwgInJlZCIpLCBsYWJlbHMgPSBjKCJQcmVzZW50IiwgIk1pc3NpbmciKSkgKw0KICBsYWJzKHRpdGxlID0gIk1pc3NpbmcgRGF0YSBIZWF0bWFwIiwgeCA9ICJWYXJpYWJsZXMiLCB5ID0gIk9ic2VydmF0aW9ucyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQojIyBEaXN0cmlidXRpb24gb2YgRGVmYXVsdCBBY2NvdW50cyANCg0KICAtIE5vIHBhcnRpY3VsYXIgdHJlbmQgZW1lcmdlcyBleGNlcHQgdGhhdCBtb3JlIGRlZmF1bHQgY2FuIGJlIHNlZW4gaW4gbG93ZXIgYWdlIGdyb3VwcywgZmVtYWxzLCBzaW5nbGVzIGFuZCBhY2NvdW50cyB3aXRob3V0IHBlcnNvbmFsIGxvYW5zDQoNCmBgYHtyfQ0KbGRkX2xvbmcgPC0gTEREICU+JQ0KICBtdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgYXMuY2hhcmFjdGVyKSkgJT4lICAjIENvbnZlcnQgYWxsIGNvbHVtbnMgdG8gY2hhcmFjdGVyDQogIHBpdm90X2xvbmdlcihjb2xzID0gLURlZmF1bHQsIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgdmFsdWVzX3RvID0gIlZhbHVlIikNCg0KIyBHZW5lcmF0ZSBkaXN0cmlidXRpb24gcGxvdHMgZm9yIGVhY2ggdmFyaWFibGUgYnkgRGVmYXVsdA0KZ2dwbG90KGxkZF9sb25nLCBhZXMoeCA9IFZhbHVlLCBmaWxsID0gYXMuZmFjdG9yKERlZmF1bHQpKSkgKw0KICBnZW9tX2JhcihhbHBoYSA9IDAuNywgcG9zaXRpb24gPSAiZG9kZ2UiKSArICAjIEJhciBwbG90IGZvciBjYXRlZ29yaWNhbCB2YWx1ZXMNCiAgZmFjZXRfd3JhcCh+IFZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIpICsgICMgU2VwYXJhdGUgcGxvdHMgZm9yIGVhY2ggdmFyaWFibGUNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgVmFyaWFibGVzIGJ5IG51bWJlciBEZWZhdWx0ZWQgQWNjb3VudHMiLA0KICAgICAgIHggPSAiVmFsdWUiLA0KICAgICAgIHkgPSAiRGVmYXVsdCIsDQogICAgICAgZmlsbCA9ICJEZWZhdWx0IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoqICoqRGlzdHJpYnV0aW9uIG9mIERlZmF1bHQgQWNjb3VudHMgYnkgR2VuZGVyIGFuZCBNYXJpdGFsIHN0YXR1cyoqIA0KDQogIC0gTm8gcGFydGljdWxhciB0cmVuZCBlbWVyZ2VzDQogIA0KYGBge3J9DQoNCmRzIDwtIExERA0KZHMgJT4lDQogIGZpbHRlcihEZWZhdWx0ICVpbiUgYygwLCAxKSkgJT4lDQogIGdyb3VwX2J5KEdlbmRlciwgTWFyaXRhbF9zdGF0dXMsIERlZmF1bHQpICU+JQ0KICBzdW1tYXJpc2UobiA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGdncGxvdChhZXMoeCA9IEdlbmRlciwgeSA9IG4sIGZpbGwgPSBmYWN0b3IoRGVmYXVsdCkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgZmFjZXRfZ3JpZCguIH4gTWFyaXRhbF9zdGF0dXMpICsgICMgU2lkZS1ieS1zaWRlIGdyYXBocw0KICBnZ3RpdGxlKCJOdW1iZXIgb2YgQWNjb3VudHMgd2l0aCBEZWZhdWx0ICgwIHZzIDEpIGJ5IEdlbmRlciBhbmQgTWFyaXRhbCBTdGF0dXMiKSArDQogIGxhYnMoZmlsbCA9ICJEZWZhdWx0IiwgeCA9ICJHZW5kZXIiLCB5ID0gIk51bWJlciBvZiBBY2NvdW50cyIpICsgIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIxIiA9ICJicm93bjIiLCAiMCIgPSAiY3lhbjQiKSkgKyAgIyBCYWxhbmNlZCBSZWQtR3JlZW4gY29sb3JzDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZSgNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0YwRjBGMCIsIGNvbG9yID0gTkEpLCAgIyBHcmF5IGluc2lkZSBncmFwaCBvbmx5DQogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAgICMgS2VlcCB4L3kgbGFiZWxzIGFuZCB0aXRsZXMgY2xlYW4gKG5vIGdyYXkpDQogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNEM0QzRDMiLCBjb2xvciA9ICIjRDNEM0QzIiksICAjIExpZ2h0ZXIgZ3JheSBmYWNldCBoZWFkZXJzDQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIsIGZhY2UgPSAiYm9sZCIpLCAgIyBCbGFjayBib2xkIHRleHQgZm9yIGhlYWRlcnMNCiAgICBwYW5lbC5zcGFjaW5nLnggPSB1bml0KDIsICJsaW5lcyIpLCAgIyBUSElDSyB3aGl0ZSBzZXBhcmF0aW9uIGJldHdlZW4gZ3JhcGhzDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjcpLCAgIyBUaGluIHdoaXRlIG1ham9yIGdyaWQgbGluZXMNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuNykgICAjIFRoaW4gd2hpdGUgbWlub3IgZ3JpZCBsaW5lcw0KICApDQoNCmBgYA0KDQojIENyZWF0aW5nIE1pc3NpbmcgYWNjb3VudHMNCg0KZm9yIHRoZSBzYWtlIG9mIHRoZSBhbmFseXNpcyB3ZSB3aWxsIGNyZWF0ZSBtaXNzaW5nIHZhbHVlcyBpbiBzYXZpbmcgYW1vdW50IGFuZCBjYWxjdWxhdGUgdGhlIG1pc3NpbmcgdmFsdWVzDQoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCmxkZG0gPC0gTEREDQpsZGRtJFNhdmluZ19hbW91bnRbc2FtcGxlKDE6MTAwMCwgOTAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQoNCmBgYA0KDQojIyBDb25maXJtaW5nIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlczoNCg0KYGBgIHtyfQ0Kc3VtKGlzLm5hKGxkZG0kU2F2aW5nX2Ftb3VudCkpDQoNCmBgYA0KDQojIEltcHV0aW5nIE1pc3NpbmcgVmFsdWVzDQoNCiMjIFBhaXJpbmcgVmFyaWFibGVzIHdpdGggTWlzc2luZyBWYWx1ZXMgd2l0aCBQcmVkaWN0aW5nIFZhcmlhYmxlcw0KDQoNCldlIHN0YXJ0IGJ5IHBhaXJpbmcgdGhlIFZhcmlhYmxlIHRvIGdldCBzZW5zZSBvZiB0aGUgcG90ZW50aWFsIHJlbGF0aW9uc2hpcC4gRnJvbSBiZWxvdyB3ZSBjYW4gc2VlIHRoYXQgQW1vdW50IGFuZCBzYXZpbmcgYWNvdW50IGhhcyBzaW1pbGFyIGRpc3RyaWJ1dGlvbi4gU2FtZSBvYnNlcnZhdGlvbiBjYW4gYmUgc2VlbiBhbHNvIHdpdGggbWFyaXRhbCBzdGF0dXMuDQoNCmBgYHtyLCBlY2hvID0gRkFMU0V9DQpsaWJyYXJ5KEdHYWxseSkNCg0KZ2dwYWlycyhsZGRtLCBjb2x1bW5zID0gYygiU2F2aW5nX2Ftb3VudCIsICJFbXBfZHVyYXRpb24iLCAiTWFyaXRhbF9zdGF0dXMiLCAiQW1vdW50IikpDQoNCmBgYA0KDQoNCiMjIEltcHV0aW5nIE1pc3NpbmcgVmFsdWVzIFVzaW5nIFJhbmRvbSBSZWdyZXNzaW9uIEltcHV0YXRpb24NCg0KICAtIEZpdCBhIGxpbmVhciBtb2RlbA0KICAtIFByZWRpY3QgU2F2aW5nX2Ftb3VudCBmb3IgdGhlIG1pc3NpbmcgdmFsdWVzDQogIC0gR2V0IHJlc2lkdWFscyBmcm9tIHRoZSBsaW5lYXIgbW9kZWwNCiAgLSBCdWlsZCB0aGUgc2NhdHRlcnBsb3QgdG8gY29tcGFyZSByZWdyZXNzaW9uIGFuZCByYW5kb20gcmVncmVzc2lvbiBtb2RlbHMNCg0KDQoNCmBgYHtyfQ0KIyBGaXQgYSBsaW5lYXIgbW9kZWwNCnByZWQubWRsIDwtIGxtKFNhdmluZ19hbW91bnQgfiBFbXBfZHVyYXRpb24gKyBNYXJpdGFsX3N0YXR1cyArIEFtb3VudCArIENyZWRpdF9zY29yZSwgDQogICAgICAgICAgICAgICBkYXRhID0gbGRkbSwgbmEuYWN0aW9uID0gbmEuZXhjbHVkZSkNCg0KIyBJZGVudGlmeSBtaXNzaW5nIHZhbHVlcyBpbiBTYXZpbmdfYW1vdW50DQptaXNzaW5nX2lkeCA8LSB3aGljaChpcy5uYShsZGRtJFNhdmluZ19hbW91bnQpKSAgIyBHZXQgbWlzc2luZyB2YWx1ZSBpbmRpY2VzDQoNCiMgUHJlZGljdCBTYXZpbmdfYW1vdW50IG9ubHkgZm9yIG1pc3NpbmcgdmFsdWVzDQpwcmVkLlNhdmluZ19hbW91bnQgPC0gcHJlZGljdChwcmVkLm1kbCwgbmV3ZGF0YSA9IGxkZG1bbWlzc2luZ19pZHgsIF0pICANCg0KIyBBc3NpZ24gcHJlZGljdGVkIHZhbHVlcyB0byBtaXNzaW5nIHJvd3MNCmxkZG0kU2F2aW5nX2Ftb3VudFttaXNzaW5nX2lkeF0gPC0gcHJlZC5TYXZpbmdfYW1vdW50DQoNCiMgRW5zdXJlIHByZWRpY3Rpb24gbGVuZ3RoIG1hdGNoZXMgbWlzc2luZyB2YWx1ZXMgY291bnQNCmlmIChsZW5ndGgocHJlZC5TYXZpbmdfYW1vdW50KSAhPSBsZW5ndGgobWlzc2luZ19pZHgpKSB7DQogICAgc3RvcCgiUHJlZGljdGlvbiBsZW5ndGggZG9lcyBub3QgbWF0Y2ggdGhlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcy4iKQ0KfQ0KDQojIEdldCByZXNpZHVhbHMgZnJvbSB0aGUgbGluZWFyIG1vZGVsDQpwcmVkLnJlc2lkIDwtIHJlc2lkKHByZWQubWRsKQ0KDQojIEVuc3VyZSBlbm91Z2ggcmVzaWR1YWxzIGFyZSBhdmFpbGFibGUgZm9yIHNhbXBsaW5nDQptMCA8LSBsZW5ndGgocHJlZC5TYXZpbmdfYW1vdW50KSAgDQppZiAobGVuZ3RoKHByZWQucmVzaWQpID49IG0wKSB7DQogICAgcHJlZC55cmFuZCA8LSBwcmVkLlNhdmluZ19hbW91bnQgKyBzYW1wbGUocHJlZC5yZXNpZCwgbTAsIHJlcGxhY2UgPSBUUlVFKQ0KfSBlbHNlIHsNCiAgICBwcmVkLnlyYW5kIDwtIHByZWQuU2F2aW5nX2Ftb3VudCAgIyBVc2UgZGV0ZXJtaW5pc3RpYyB2YWx1ZXMgYXMgZmFsbGJhY2sNCn0NCg0KIyBBc3NpZ24gdGhlIGZpbmFsIGltcHV0ZWQgdmFsdWVzDQpsZGRtJFNhdmluZ19hbW91bnRbbWlzc2luZ19pZHhdIDwtIHByZWQueXJhbmQNCg0KIyBDaGVjayBpZiBhbGwgbWlzc2luZyB2YWx1ZXMgYXJlIGltcHV0ZWQNCnJlbWFpbmluZ19uYSA8LSBzdW0oaXMubmEobGRkbSRTYXZpbmdfYW1vdW50KSkNCg0KDQojIFNjYXR0ZXIgcGxvdA0KcGxvdChsZGRtJENoZWNraW5nX2Ftb3VudCwgbGRkbSRTYXZpbmdfYW1vdW50LCANCiAgICAgbWFpbiA9ICJTYXZpbmdfYW1vdW50IHZzIENoZWNraW5nX2Ftb3VudCIsDQogICAgIHhsYWIgPSAiQ2hlY2tpbmcgQW1vdW50IiwgeWxhYiA9ICJTYXZpbmcgQW1vdW50IiwgY29sID0gImdyYXkiKQ0KDQojIEFkZCByZWdyZXNzaW9uLWltcHV0ZWQgcG9pbnRzIChyZWQpDQpwb2ludHMobGRkbSRDaGVja2luZ19hbW91bnRbbWlzc2luZ19pZHhdLCBwcmVkLlNhdmluZ19hbW91bnQsIHBjaCA9IDE5LCBjb2wgPSAicmVkIikNCg0KIyBBZGQgcmFuZG9tIHJlZ3Jlc3Npb24taW1wdXRlZCBwb2ludHMgKGJsdWUpDQpwb2ludHMobGRkbSRDaGVja2luZ19hbW91bnRbbWlzc2luZ19pZHhdLCBwcmVkLnlyYW5kLCBwY2ggPSAxOSwgY29sID0gImJsdWUiKQ0KDQojIEFkZCBsZWdlbmQNCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IGMoIlJlZ3Jlc3Npb24gSW1wdXRhdGlvbiIsICJSYW5kb20gUmVncmVzc2lvbiBJbXB1dGF0aW9uIiksDQogICAgICAgY29sID0gYygicmVkIiwgImJsdWUiKSwgcGNoID0gcmVwKDE5LCAyKSwgYnR5ID0gIm4iLCBjZXggPSAwLjgpDQpgYGANCg0KDQpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQ0KDQptaXNzaW5nX2lkeCA8LSB3aGljaChpcy5uYShsZGRtJFNhdmluZ19hbW91bnQpKQ0KcHJlZGljdGVkX3ZhbHVlcyA8LSBwcmVkaWN0KHByZWQubWRsLCBuZXdkYXRhID0gbGRkbVttaXNzaW5nX2lkeCwgXSkNCg0KIyBDb3VudCBob3cgbWFueSBwcmVkaWN0aW9ucyBhcmUgTkENCnN1bShpcy5uYShwcmVkaWN0ZWRfdmFsdWVzKSkgICMgU2hvdWxkIGJlIDAgaWYgd29ya2luZyBjb3JyZWN0bHkNCnByaW50KHByZWRpY3RlZF92YWx1ZXMpICAjIFZpZXcgcHJlZGljdGlvbnMNCg0KYGBgDQoNCiAgLSBDb25maXJtaW5nIHRoYXQgYWxsIG1pc3NpbmcgdmFsdWVzIHdlcmUgYXNzaWduZWQgaW1wdXRlZCB2YWx1ZXMuIHdlIGhhdmUgemVybyBtaXNzaW5nIHZhbHVlcyBhZnRlciBpbXB1dGFpb24gYXMgcGVyIGJlbG93Lg0KICANCmBgYHtyLCBpbmNsdWRlID1GQUxTRX0NCg0Kc3VtKGlzLm5hKGxkZG0kU2F2aW5nX2Ftb3VudCkpICAjIFNob3VsZCBiZSAwIGlmIGFsbCB3ZXJlIGltcHV0ZWQNCmxkZG0kU2F2aW5nX2Ftb3VudFttaXNzaW5nX2lkeF0gPC0gcHJlZGljdGVkX3ZhbHVlcw0KDQpgYGANCg0KDQpgYGB7cn0NCiMgVXBkYXRlIHRoZSBkYXRhc2V0IHdpdGggdGhlIGltcHV0ZWQgdmFsdWVzIGZvciBTYXZpbmdfYW1vdW50DQptaXNzaW5nX2lkeCA8LSB3aGljaChpcy5uYShsZGRtJFNhdmluZ19hbW91bnQpKQ0KbGRkbSRTYXZpbmdfYW1vdW50W21pc3NpbmdfaWR4XSA8LSBwcmVkLlNhdmluZ19hbW91bnRbbWlzc2luZ19pZHhdDQoNCiMgQ291bnQgbWlzc2luZyB2YWx1ZXMgYWZ0ZXIgaW1wdXRhdGlvbg0KbWlzc2luZ19hZnRlciA8LSBzdW0oaXMubmEobGRkbSRTYXZpbmdfYW1vdW50KSkNCnByaW50KHBhc3RlKCJNaXNzaW5nIHZhbHVlcyBhZnRlciBpbXB1dGF0aW9uOiIsIG1pc3NpbmdfYWZ0ZXIpKQ0KDQpgYGANCg0KDQoNCiMgRml0dGluZyBUaGUgTG9nZXN0aWMgUmVncmVzc2lvbiBNb2RlbCBGb3IgRGVmYXVsdCANCg0KYGBge3J9DQoNCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyeQ0KDQoNCiMgRml0IGEgZnVsbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHdpdGggYWxsIHByZWRpY3RvcnMNCmZ1bGxfbW9kZWwgPC0gZ2xtKERlZmF1bHQgfiAuLCBkYXRhID0gbGRkbSwgZmFtaWx5ID0gYmlub21pYWwpDQoNCiMgUGVyZm9ybSBzdGVwd2lzZSBzZWxlY3Rpb24gKGRlZmF1bHQgaXMgYmFja3dhcmQgZWxpbWluYXRpb24pDQpzdGVwX21vZGVsIDwtIHN0ZXAoZnVsbF9tb2RlbCwgZGlyZWN0aW9uID0gImJvdGgiKQ0KDQpgYGANCg0KDQojIFByZWRpY3RpbmcgdGhlIERlZmF1bHQNCg0KRnJvbSB0aGUgYWxsIG1vZGVsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gc3RlcCBhYm92ZSwgd2UgZ290IG91ciBwcmltYXJ5IG1vZGVsIGFuZCB3ZSB1c2UgY3Jvc3MtdmFsaWRhdGlvbiBpcyB0byBjb21wYXJlIHBlcmZvcm1hbmNlIGJldHdlZW4gdGhpcyBtb2RlbCBhbmQgYW5vdGhlciBjaGFsbGVuZ2VyIG1vZGVsIHRvIGFycml2ZSBhdCBvcHRpbWFsIG1vZGVsIHNlbGVjdGlvbiwgd2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyB0d28gbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgYW5kIHVzZSB0aGUgNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24gbWV0aG9kIHRvIGlkZW50aWZ5IHRoZSBvcHRpbWFsIG1vZGVsLg0KDQogIC0gTW9kZWwgMTogICBEZWZhdWx0ID0gzrEwKyDOsTHDl0NoZWNraW5nX2Ftb3VudCArIM6xMsOXVGVybSArIM6xM8OXQ3JlZGl0X3Njb3JlICsgzrE0w5dQZXJzb25hbF9sb2FuICsgzrE1w5dIb21lX2xvYW4gKyAgICAgzrE2w5dFZHVjYXRpb25fbG9hbiArIM6xN8OXRW1wX3N0YXR1cyArIM6xOMOXU2F2aW5nX2Ftb3VudCArIM6xOcOXQWdlIA0KDQoNCiAgLSBNb2RlbCAyOiAgIERlZmF1bHQgPSDOsTArIM6xMcOXQ3JlZGl0X3Njb3JlICsgzrEyw5dIb21lX2xvYW4gKyDOsTPDl0VkdWNhdGlvbl9sb2FuICsgzrE0w5dFbXBfc3RhdHVzICsgzrE1w5dTYXZpbmdfYW1vdW50ICsgzrE2w5dBZ2UgDQoNCk1vZGVsIFNlbGVjdGlvbiB2aWEgNS1mb2xkIENyb3NzLVZhbGlkYXRpb24NCg0KICAtIFdlIHVzZSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB0byBzZWxlY3QgdGhlIGJldHRlciBvbmUgZnJvbSBNb2RlbCAxIGFuZCBNb2RlbCAyLiA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbjogc3BsaXR0aW5nIHRoZSB0cmFpbmluZyBzZXQgaW50byA1IGZvbGRzIHdpdGggZXF1YWwgc2l6ZSB0byBwZXJmb3JtIGNyb3NzLXZhbGlkYXRpb24uDQoNCiAgLSBEYXRhIFNwbGl0dGluZzogVXNpbmcgcmFuZG9tIHNwbGl0dGluZyB0byBwYXJ0aXRpb24gdGhlIGRhdGEgaW50byB0cmFpbmluZyBzZXQgKDcwMCkgYW5kIHRlc3Rpbmcgc2V0ICgzMDApLg0KDQogIC0gQ2hvb3NlIFBlcmZvcm1hbmNlIE1lYXN1cmU6IFRoZSByZXN1bHRpbmcgbW9kZWwgd2lsbCBiZSB1c2VkIHRvIHByZWRpY3QgdGhlIERlZmF1bHQuIFdlIHVzZSB0aGUgbWVhbiBzcXVhcmUgZXJyb3IgdG8gbWVhc3VyZSB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4NCg0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgZGF0YQ0KZGF0YShMREQpDQojTEREJGFtIDwtIGFzLmZhY3RvcihtdGNhcnMkYW0pICMgQ29udmVydCB0byBmYWN0b3IgZm9yIGxvZ2lzdGljIHJlZ3Jlc3Npb24NCg0KIyBGaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsDQptb2RlbF9nbG0gPC0gZ2xtKERlZmF1bHQgfiBDaGVja2luZ19hbW91bnQgKyBUZXJtICsgQ3JlZGl0X3Njb3JlICsgUGVyc29uYWxfbG9hbiArIA0KICAgIEhvbWVfbG9hbiArIEVkdWNhdGlvbl9sb2FuICsgRW1wX3N0YXR1cyArIFNhdmluZ19hbW91bnQgKyANCiAgICBBZ2UsIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gTEREKQ0KDQojIFN1bW1hcnkgb2YgdGhlIG1vZGVsDQpzdW1tYXJ5KG1vZGVsX2dsbSkNCg0KYGBgDQoNCmBgYHtyfQ0KDQojIEluc3RhbGwgcGxvdGx5IGlmIG5vdCBhbHJlYWR5IGluc3RhbGxlZA0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJwbG90bHkiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCn0NCg0KIyBMb2FkIHRoZSBwYWNrYWdlDQpsaWJyYXJ5KHBsb3RseSkNCg0KIyB1c2luZyBzYW1wbGUoKSB0byBwZXJmb3JtIHJhbmRvbSBzcGxpdHRpbmcNCnRyYWluLklEID0gc2FtcGxlKDE6ZGltKGxkZG0pWzFdLCA3MDAsIHJlcGxhY2UgPSBGQUxTRSkgICMgd2l0aG91dCByZXBsYWNlbWVudA0KIyB0cmFpbmluZyBzZXQNCnRyYWluID0gbGRkbVt0cmFpbi5JRCxdDQp0ZXN0ID0gbGRkbVstdHJhaW4uSUQsXQ0KIyMgc3BsaXR0aW5nIHRoZSB0cmFpbiBzZXQgaW50byA1IGZvbGRzIHRvIHRyYWluIGFuZCB2YWxpZGF0ZSB0aGUgY2FuZGlkYXRlIG1vZGVscw0KTiA9IGRpbSh0cmFpbilbMV0gICAjIHNpemUgb2YgdHJhaW5pbmcgZGF0YQ0KayA9IDUgICAgICAgICAgICAgICAjIG51bWJlciBvZiBmb2xkcw0KZmxkLm4gPSBjZWlsaW5nKE4vaykNCk1TRS5tMSA9IE5VTEwgICAgICAgIyBudWxsIHZlY3RvciB0byBzdG9yZSBNU0UNCk1TRS5tMiA9IE5VTEwgICAgICANCmZvciAoaSBpbiAxOmspew0KICB2YWxpZC5JRCA9ICgoaS0xKSpmbGQubiArMSk6KGkqZmxkLm4pICAjIG9ic2VydmF0aW9uIElEIGZvciB0aGUgaS10aCB2YWxpZGF0aW9uIHNldCANCiAgdmFsaWQuc2V0ID0gdHJhaW5bdmFsaWQuSUQsIF0NCiAgdHJhaW4uc2V0ID0gdHJhaW5bLXZhbGlkLklELF0NCiAgIyMgZml0dGluZyB0d28gY2FuZGlkYXRlIG1vZGVscyB3aXRoIGNvbWJpbmVkIDQgZm9sZHMgb2YgZGF0YSBzZXQNCiAgTTAxID0gZ2xtKERlZmF1bHQgfiBDaGVja2luZ19hbW91bnQgKyBUZXJtICsgQ3JlZGl0X3Njb3JlICsgUGVyc29uYWxfbG9hbiArIEhvbWVfbG9hbiArIEVkdWNhdGlvbl9sb2FuICsgRW1wX3N0YXR1cyArIFNhdmluZ19hbW91bnQgKyBBZ2UsIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gdHJhaW4uc2V0KQ0KICBNMDIgPSBnbG0oRGVmYXVsdCB+IENyZWRpdF9zY29yZSArIEhvbWVfbG9hbiArIEVkdWNhdGlvbl9sb2FuICsgRW1wX3N0YXR1cyArIFNhdmluZ19hbW91bnQgKyBBZ2UsIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gdHJhaW4uc2V0KQ0KICAjIyBQcmVkaWN0aW5nIERlZmF1bHQgdXNpbmcgdGhlIHR3byBjYW5kaWRhdGUgbW9kZWxzIGJhc2VkIG9uIHRoZSB2YWxpZGF0ZSBzZXQNCiAgcHJlZE0wMSA9IHByZWRpY3QoTTAxLCBuZXdkYXRhID0gdmFsaWQuc2V0KQ0KICBwcmVkTTAyID0gcHJlZGljdChNMDIsIG5ld2RhdGEgPSB2YWxpZC5zZXQpDQogICMjIGNhbGN1bGF0aW5nIHRoZSBNU0UgYXNzb2NpYXRlZCB3aXRoIHRoZSB0d28gbW9kZWxzDQogIE1TRS5tMVtpXSA9IG1lYW4oKHByZWRNMDEgLSB2YWxpZC5zZXQkRGVmYXVsdCleMikNCiAgTVNFLm0yW2ldID0gbWVhbigocHJlZE0wMiAtIHZhbGlkLnNldCREZWZhdWx0KV4yKQ0KfQ0KIyMgZGVmaW5lIGEgZGF0YSBmcmFtZSB0byBzdG9yZSB0aGUgTVNFIG9mIHRoZSBjYW5kaWRhdGUgbW9kZWxzDQojIyANCk1TRSA9IGRhdGEuZnJhbWUoZm9sZCA9IHJlcCgxOmssMiksIE1TRSA9IGMoTVNFLm0xLCBNU0UubTIpLCB0eXBlPWMocmVwKCJNb2RlbCAxIixrKSwgcmVwKCJNb2RlbCAyIiwgaykpKQ0KDQojIyBsaW5lIHBsb3RzIG9mIHRoZSANCmN2cGxvdCA9IGdncGxvdChkYXRhID0gTVNFLCBhZXMoeD1mb2xkLCB5PU1TRSwgY29sb3IgPSB0eXBlKSkgKw0KICAgICAgICAgZ2VvbV9saW5lKCkgKw0KICAgICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgICAgIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA2KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5bGltID0gYygwLDQwKSkgKw0KICAgICAgICAgZ2VvbV90ZXh0KG1hcHBpbmcgPSBhZXMoeD0yLjAsIHk9NSwgDQogICAgICAgICAgICAgICAgICBsYWJlbD1wYXN0ZSgiTW9kZWwgMSBNZWFuIE1TRTogPSAiLCByb3VuZChtZWFuKE1TRS5tMSksMyksICIiKSksIA0KICAgICAgICAgICAgICAgICAgIGhqdXN0PTApICsNCiAgICAgICAgIGdlb21fdGV4dChtYXBwaW5nID0gYWVzKHg9Mi4wLCB5PTEwLCANCiAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJNb2RlbCAyIE1lYW4gTVNFOiA9ICIsIHJvdW5kKG1lYW4oTVNFLm0yKSwzKSwgIiIpKSwgDQogICAgICAgICAgICAgICAgICAgaGp1c3Q9MCkgKyANCiAgICAgICAgIGdndGl0bGUoIkxpbmUgcGxvdHMgb2YgTVNFIGNhbmRpZGF0ZSBNb2RlbHMgYWNyb3NzIGZvbGRzIikgKw0KICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLDEsMSwxKSwgImNtIikpDQpnZ3Bsb3RseShjdnBsb3QpDQoNCmBgYA0KDQpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQ0KDQojIERlYnVnZ2luZyB0aGUgY29kZSBhbmQgdGhlIG91dGNvbWUgdG8gbWFrZSBzdXJlIG5vdGhpbmcgbWlzc2luZw0KDQpzdW0oaXMubmEodmFsaWQuc2V0JERlZmF1bHQpKSAjIHJldHVybiBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gRGVmYXVsdA0Kc3VtKGlzLm5hKHByZWRNMDEpKSAjIHJldHVybiBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gcHJlZE0wMQ0Kc3VtKGlzLm5hKHByZWRNMDIpKSAjIHJldHVybiBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gcHJlZE0wMQ0KcHJpbnQoTVNFLm0xKSANCnByaW50KE1TRS5tMikNCnN0cih2YWxpZC5zZXQkRGVmYXVsdCkNCnN1bShpcy5uYSh2YWxpZC5zZXQkRGVmYXVsdCkpICAjIEVuc3VyZSBubyBtaXNzaW5nIHZhbHVlcw0Kc3RyKE1TRSkgICMgQ2hlY2sgc3RydWN0dXJlDQpoZWFkKE1TRSkgIyBQcmV2aWV3IGZpcnN0IGZldyByb3dzDQpwcmludChjdnBsb3QpDQpzdW0oaXMubmEoTVNFJE1TRSkpDQoNCmBgYA0KDQojIENvbmNsdXNpb24NCg0KZnJvbSB0aGUgYWJvdmUgd2UgY2FuIHNlZSB0aGF0IE1vZGVsIDIgaGFzIGxvd2VyIE1TRSBhbmQgbW9yZSBzaW1wbGVyLCB3aXRoIDYgcHJlZGljdG9ycywgY29tcGFyZWQgdG8gbW9kZWwgMSBhbmQgaGVuY2Ugd2UgY2hvb3NlIG1vZGVsIDIgYXMgRGVmYXVsdCBwcmVkaWN0b3IuDQoNCg0K